/**
 * @file select
 * @author liaoxuezhi@baidu.com
 *
 * https://github.com/JedWatson/react-select
 */
/* eslint fecs-indent: [0, "space", 2, 2] */

import * as React from 'react';
import {
  PureComponent,
  PropTypes
} from 'react';
import {fetch, filter} from '../../renderer';
import ReactSelect from 'react-select';
import * as assign from 'object-assign';
import * as cx from 'classnames';
import {findByAttrValue} from '../../../util/helper';
import {filterOptions} from '../util';
import {Enhance as FormItem} from '../form-item';
// @require 'react-select/dist/react-select.css';
// jest 不能支持这种写法
// import 'react-select/dist/react-select.css';
import {findDOMNode} from 'react-dom';
import {evalReturnExpression} from '../../util';

@FormItem({
  type: 'select',
  test: (path, config) => /(?:^|\/)form-item$/.test(path) && (config.type === 'select' || config.type === 'multi-select'),
  validateSchema: 'http://amis.baidu.com/schemas/form.json#/definitions/formItem'
})
export default class SelectFormItem extends PureComponent {
  static propTypes = {
    className: PropTypes.string,
    formMode: PropTypes.string,
    formHorizontal: PropTypes.object,
    joinValues: PropTypes.bool.isRequired,
    delimiter: PropTypes.string.isRequired
  };

  static defaultProps = {
    value: '',
    placeholder: '请选择',
    joinValues: true,
    delimiter: ',',
    clearable: false,
    searchable: true
  };

  static contextTypes = {
    combo: PropTypes.object
  };

  constructor(props) {
    super(props);

    this.state = {
      options:  props.sourceLocalData ? filterOptions(props.sourceLocalData, props.data) : (filterOptions(props.options, props.data) || []),
      rawOptions: null
    };

    this.changeValue = this.changeValue.bind(this);
    this.loadRemote = this.loadRemote.bind(this);
  }

  componentWillMount() {
    const {
      unique,
      name,
      sourceLocalData,
      source
    } = this.props;
    const combo = this.context.combo;

    if (unique && combo && (sourceLocalData || !source)) {
      combo.registerUnique(name, this);
      this.setState({
        options: combo.getRestOptions(name, this)
      });
    }
  }

  componentWillReceiveProps(nextProps) {
    const props = this.props;
    const combo = this.context.combo;

    if (props.sourceFetching && !nextProps.sourceFetching && nextProps.sourceData) {
      const sourceData = nextProps.sourceData;
      let options = filterOptions(sourceData && sourceData.hasOwnProperty('options') ? sourceData.options : sourceData, nextProps.data);

      if (nextProps.unique && combo) {
        combo.registerUnique(nextProps.name, this, options);
        options = combo.getRestOptions(nextProps.name, this);
      }

      this.setState({
        options,
        rawOptions: sourceData && sourceData.hasOwnProperty('options') ? sourceData.options : sourceData
      }, () => sourceData.hasOwnProperty('value') && this.props.setValue(sourceData.value));
    } else if (props.options !== nextProps.options || props.sourceLocalData !== nextProps.sourceLocalData) {
      const sourceLocalData = nextProps.sourceLocalData;
      let options = filterOptions(sourceLocalData || nextProps.options, nextProps.data) || [];

      if (nextProps.unique && combo) {
        combo.registerUnique(nextProps.name, this, options);
        options = combo.getRestOptions(nextProps.name, this);
      }

      this.setState({
        options
      });
    } else if (props.data !== nextProps.data) {
      const sourceLocalData = nextProps.sourceLocalData;
      let options = filterOptions(this.state.rawOptions || sourceLocalData || nextProps.options,
        nextProps.data);

      if (nextProps.unique && combo) {
        combo.registerUnique(nextProps.name, this, options);
        options = combo.getRestOptions(nextProps.name, this);
      }

      this.setState({
        options
      });
    }
  }

  componentDidUpdate(prevProps) {
    const props = this.props;

    if (props.autoComplete) {
      const prev = filter(prevProps.autoComplete.url || prevProps.autoComplete, prevProps.data);
      const next = filter(props.autoComplete.url || props.autoComplete, props.data);

      if (prev !== next) {
        this.props.setValue('');
        this.input && this.input.loadOptions(this.input.state.inputValue);
      }
    }
  }

  componentWillUnmount() {
    const {
      unique,
      name
    } = this.props;
    const combo = this.context.combo;

    if (unique && combo) {
      combo.unRegisterUnique(name, this);
    }
  }

  changeValue(newValue) {
    const {
      joinValues,
      delimiter,
      multiple,
      type
    } = this.props;

    if (joinValues) {
      const multi = multiple || type === 'multi-select' || this.props.multi;

      if (multi) {
        newValue = newValue && newValue.map(item => item.value).join(delimiter);
      } else {
        newValue = newValue && newValue.value;
      }
    }

    this.props.setValue(newValue);
  }

  loadRemote(input) {
    const {
      autoComplete,
      data
    } = this.props;

    return fetch(autoComplete, {...data, term: input})
      .then(ret => new Promise(resolve => {
        const options = filterOptions(ret.data);
        let mergedOptions = options;

        this.setState({
          options: mergedOptions,
          rawOptions: mergedOptions
        }, () => resolve({
          options: options
        }));
      }))
  }

  focus() {
    this.input && this.input.focus();
  }

  render() {
    let {
      name,
      type,
      joinValues,
      delimiter,
      placeholder,
      disabled,
      clearable,
      autoComplete,
      searchable,
      inline,
      multiple,
      inputClassName,
      width,
      height,
      style,
      cache
    } = this.props;

    const options = this.state.options;
    const multi = multiple || type === 'multi-select';
    let value = this.props.getValue();
    value = typeof value === 'number' ? String(value) : value;


    if (multi && value && typeof value === 'string' && joinValues) {
      value = filterOptions(value.split(delimiter)).map(item => findByAttrValue(options, 'value', item.value) || item);
    } else if (!multi && joinValues) {
      value = value && String(value);
    }

    if (width || height) {
      style = assign({}, style, {
        width,
        height
      });

      width && (style.minWidth = width);
    }

    const selectProps = {
      name, placeholder, multi,
      options: options || [],
      autosize: false,
      value: value,
      onChange: this.changeValue,
      disabled,
      clearable,
      searchable,
      loadOptions: autoComplete ? this.loadRemote : null,
      className: inputClassName || inline && 'w-auto sm-inline',
      wrapperStyle: style,
      cache
    };
    const SelectComponent = autoComplete ? ReactSelect.Async : ReactSelect;
    return (
      <SelectComponent
          key="input"
          {...selectProps}
          ref={input => this.input = input}
          noResultsText="无数据"
          searchPromptText="输入内容进行检索"
          loadingPlaceholder="加载中"
        />
    );
  }
}
